package com.bart.hsp.reflect

// 反射
    //scala.reflect.api.Mirrors#InstanceMirror
    //scala.reflect.api.Mirrors#MethodMirror
    //scala.reflect.api.Mirrors#FieldMirror
    //scala.reflect.api.Mirrors#ClassMirror
    //scala.reflect.api.Mirrors#ModuleMirror

case class Employee(name:String){
  def showName = print(">>>" + name)
}

//******************  Main  *********************
object Main extends App {
  //引入反射类型转换
  import scala.reflect.runtime.universe._

  //用于获取实例的类型信息的方法
  def getTypeTag[T:TypeTag](a:T) = typeTag[T]

  val emp = Employee("China")

  val classType = getTypeTag(emp).tpe
  // classType: reflect.runtime.universe.Type = Employee

  val classType2 = getTypeTag(emp)
  // classType2: reflect.runtime.universe.TypeTag[Employee] = TypeTag[Employee]

  val classType3 = typeOf[Employee]
  // classType3: reflect.runtime.universe.Type = Employee

  // 获取运行期镜像
  val runtimeMirrorRef = runtimeMirror(getClass.getClassLoader)

  //获取类镜像，类似于Java中的Class<?>
  val classMirrorRef = runtimeMirrorRef.reflectClass(classType.typeSymbol.asClass)
  //classMirrorRef: reflect.runtime.universe.ClassMirror = class mirror for Employee (bound to null)

  //获取class里面定义的构造器方法
  val methodConstructorRef = classType.decl(termNames.CONSTRUCTOR).asMethod
  //methodConstructorRef: reflect.runtime.universe.MethodSymbol = constructor Employee

  //获取类实例中定义的构造函数引用
  val constructorRef = classMirrorRef.reflectConstructor(methodConstructorRef)
  //constructorRef: reflect.runtime.universe.MethodMirror = constructor mirror for def <init>(name: String): Employee (bound to null)

  //构造对象
  val employee = constructorRef("Mike")
  //employee: Any = Employee(Mike)

  //获取name属性标识
  val nameTermSymb = classType.decl(TermName("name")).asTerm
  // nameTermSymb: reflect.runtime.universe.TermSymbol = value name

  //获取showName方法标识
  val showNameTermSymb = classType.decl(TermName("showName")).asMethod
  //showNameTermSymb: reflect.runtime.universe.MethodSymbol = method showName

  // 运行时会获得具体得对象运行时得字段、方法得对应信息
  // eg： Java中 Field f = obj.getClass().getField("name") Object v = f.get(obj)
  // 这种在scala中通过 3) 和 4) 方式完成
  //1)获取运行时的实例引用
  val instanceMirrorRef = runtimeMirrorRef.reflect(employee)
  //instanceMirrorRef: reflect.runtime.universe.InstanceMirror = instance mirror for Employee(Mike)

  //2)获取运行时的属性引用
  val fieldMirrorRef = instanceMirrorRef.reflectField(nameTermSymb)
  //fieldMirrorRef: reflect.runtime.universe.FieldMirror = field mirror for private[this] val name: String (bound to Employee(Mike))

  //3)获取运行时的方法引用，类似Java得cls.getDeclaredMethod("showName")
  val methodMirrorRef = instanceMirrorRef.reflectMethod(showNameTermSymb)
  //methodMirrorRef: reflect.runtime.universe.MethodMirror = method mirror for def showName: scala.Unit (bound to Employee(Mike))

  //4)读取属性
  println(fieldMirrorRef.get)
  //Mike

  //5)调用方法
  methodMirrorRef.apply()
  //>>>Mikeres1: Any = ()

}
